package com.blacktech.dbu.auth.security;

import com.blacktech.dbu.auth.service.AuthorizationService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;

/**
 * 数据权限切面
 * 处理@DataPermission注解的权限验证
 *
 * @author Yanyunsong
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class DataPermissionAspect {

    private final ExpressionParser expressionParser = new SpelExpressionParser();
    private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    private final AuthorizationService authorizationService;

    @Around("@annotation(dataPermission)")
    public Object checkDataPermission(ProceedingJoinPoint joinPoint, DataPermission dataPermission) throws Throwable {
        log.debug("检查数据权限: {}.{}", joinPoint.getSignature().getDeclaringType().getSimpleName(), joinPoint.getSignature().getName());

        // 获取当前用户名
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        if (username == null) {
            throw new SecurityException("用户未登录");
        }

        // 获取请求对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

        // 创建SpEL表达式上下文
        EvaluationContext context = createEvaluationContext(joinPoint, request, username);

        // 提取产品ID（优先使用SpEL表达式）
        Long productId = extractProductId(context, dataPermission, request, joinPoint.getArgs());
        if (productId == null) {
            throw new IllegalArgumentException("无法获取产品ID");
        }

        // 使用统一的数据权限服务检查权限
        boolean hasPermission = authorizationService.hasPermission(username, productId, dataPermission.required());
        if (!hasPermission) {
            log.warn("用户 {} 没有产品 {} 的 {} 数据权限，访问被拒绝", username, productId, dataPermission.required());
            throw new SecurityException(dataPermission.message());
        }

        log.debug("用户 {} 通过产品 {} 的 {} 数据权限检查", username, productId, dataPermission.required());
        return joinPoint.proceed();
    }

    /**
     * 创建SpEL表达式上下文
     */
    private EvaluationContext createEvaluationContext(ProceedingJoinPoint joinPoint, HttpServletRequest request, String username) {
        StandardEvaluationContext context = new StandardEvaluationContext();

        // 添加方法参数
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
        Object[] args = joinPoint.getArgs();

        if (parameterNames != null) {
            for (int i = 0; i < parameterNames.length; i++) {
                context.setVariable(parameterNames[i], args[i]);
            }
        }

        // 添加HTTP请求对象（避免与方法参数中的"request"变量冲突）
        context.setVariable("httpRequest", request);

        // 添加当前用户名
        context.setVariable("currentUsername", username);

        return context;
    }

    /**
     * 提取产品ID（优先使用SpEL表达式）
     */
    private Long extractProductId(EvaluationContext context, DataPermission dataPermission, HttpServletRequest request, Object[] args) {
        // 优先使用SpEL表达式
        if (StringUtils.hasText(dataPermission.productId())) {
            try {
                Expression expression = expressionParser.parseExpression(dataPermission.productId());
                Object value = expression.getValue(context);
                if (value != null) {
                    return ((Number) value).longValue();
                }
            } catch (Exception e) {
                log.warn("SpEL表达式解析产品ID失败: {}, error: {}", dataPermission.productId(), e.getMessage());
            }
        }
        return null;
    }
}